package org.spring4speedtracer.perform;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.spring4speedtracer.collect.TraceCollector;
import org.spring4speedtracer.collect.TraceCollector.StepType;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Abstract aspect for gathering trace metrics around Spring components.
 * Pointcuts to configure tracing must be provided by the extending class.
 * 
 * @author Dustin
 * 
 */
public abstract class AspectTracePerformer implements TracePerformer {

	private static final Logger LOG = Logger
			.getLogger(AspectTracePerformer.class);

	@Autowired
	protected TraceCollector traceCollector;

	protected ThreadLocal<Boolean> enabled = new ThreadLocal<Boolean>();

	@Override
	public void setEnabled(boolean enabled) {
		this.enabled.set(Boolean.valueOf(enabled));
	}

	/**
	 * Pointcut determining what is traced, usually by combining other
	 * pointcuts.
	 */
	public abstract void traceable();

	/**
	 * Advice that performs a trace around an invocation, by default using the
	 * tracelabe() pointcut.
	 * 
	 * @param pjp
	 *            proceeding join point
	 * @return the return value from the target, if any
	 * @throws Throwable
	 *             anything thrown by the target
	 */
	@Around("traceable()")
	public Object trace(ProceedingJoinPoint pjp) throws Throwable {
		if (Boolean.TRUE.equals(enabled.get())) {
			try {
				traceCollector.beginStep(pjp.getSignature().getDeclaringType()
						.getSimpleName()
						+ "." + pjp.getSignature().getName(), StepType.METHOD,
						pjp.getSignature().getDeclaringTypeName(), pjp
								.getSignature().getName(), 0);
			} catch (Throwable t) {
				LOG.error("Begin step failed", t);
			}
			try {
				return pjp.proceed();
			} finally {
				try {
					traceCollector.completeStep();
				} catch (Throwable t) {
					LOG.error("Complete step failed", t);
				}
			}
		} else {
			return pjp.proceed();
		}
	}
}